mod code;

pub use code::*;
use std::error::Error;
use std::fmt;
use std::fmt::Display;

/// 状态码扩展信息
#[derive(Debug)]
pub struct StateCodeError {
    /// 状态码
    code: StateCode,
    err: Option<Box<dyn Error>>,
    /// 会返回给前端的错误信息
    message: Option<String>,
}

impl StateCodeError {
    /// 新建对象
    pub fn from_code_str(code: StateCode, message: String) -> StateCodeError {
        StateCodeError {
            code,
            message: Some(message),
            err: None,
        }
    }

    /// 新建对象
    pub fn from_err(err: impl Error + 'static) -> StateCodeError {
        StateCodeError {
            code: StateCode::Error,
            err: Some(Box::new(err)),
            message: None,
        }
    }

    /// 新建对象
    pub fn from_code_err(code: StateCode, err: impl Error + 'static) -> StateCodeError {
        StateCodeError {
            code,
            err: Some(Box::new(err)),
            message: None,
        }
    }

    /// 新建对象
    pub fn from_code_err_message(
        code: StateCode,
        err: impl Error + 'static,
        message: &str,
    ) -> StateCodeError {
        StateCodeError {
            code,
            err: Some(Box::new(err)),
            message: Some(message.to_string()),
        }
    }

    /// 新建对象
    pub fn new_with_err_message(
        code: StateCode,
        err: Option<Box<dyn Error>>,
        message: Option<String>,
    ) -> StateCodeError {
        StateCodeError { code, message, err }
    }

    /// 设置用于返回给前端的消息
    pub fn set_message(&mut self, message: String) -> &mut Self {
        self.message = Some(message);
        self
    }

    /// 设置状态码
    pub fn code(&self) -> StateCode {
        self.code
    }

    /// 获取返回给前端的消息,如果没有设置，则会取状态码的消息
    pub fn message(&self) -> String {
        match self.message.as_ref() {
            Some(val) => val.clone(),
            None => self.code.message().to_string(),
        }
    }

    /// 内部存储的错误信息
    pub fn err(&self) -> &Option<Box<dyn Error>> {
        &self.err
    }
}

unsafe impl Send for StateCodeError {}
unsafe impl Sync for StateCodeError {}

impl Display for StateCodeError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if self.err.is_some() && self.message.is_some() {
            return write!(
                f,
                "code:{} err:{} message:{}",
                self.code,
                self.err.as_ref().unwrap(),
                self.message.as_ref().unwrap()
            );
        }
        if self.err.is_some() && self.message.is_none() {
            return write!(f, "code:{} err:{}", self.code, self.err.as_ref().unwrap());
        }
        if self.err.is_none() && self.message.is_some() {
            return write!(
                f,
                "code:{} message:{}",
                self.code,
                self.message.as_ref().unwrap()
            );
        }

        return write!(f, "{}", self.code);
    }
}

impl warp::reject::Reject for StateCodeError {}

impl From<StateCode> for StateCodeError {
    fn from(val: StateCode) -> Self {
        Self::new_with_err_message(val, None, None)
    }
}
